use crate::ast::*;
use crate::error::{CompilerError, Result};
#[allow(unused_imports)]
use inkwell::types::BasicTypeEnum;
use inkwell::values::{BasicValueEnum, IntValue};
use inkwell::IntPredicate;
#[allow(unused_imports)]
use inkwell::AddressSpace;

use super::super::LLVMBackend;

impl<'ctx> LLVMBackend<'ctx> {
    fn normalize_int_operands(
        &self,
        l: IntValue<'ctx>,
        r: IntValue<'ctx>,
    ) -> Result<(IntValue<'ctx>, IntValue<'ctx>)> {
        let l_width = l.get_type().get_bit_width();
        let r_width = r.get_type().get_bit_width();
        if l_width != r_width {
            if l_width < r_width {
                Ok((
                    self.builder.build_int_s_extend(l, r.get_type(), "extend")?,
                    r,
                ))
            } else {
                Ok((
                    l,
                    self.builder.build_int_s_extend(r, l.get_type(), "extend")?,
                ))
            }
        } else {
            Ok((l, r))
        }
    }

    fn ensure_bool_value(
        &mut self,
        value: BasicValueEnum<'ctx>,
        name: &str,
    ) -> Result<IntValue<'ctx>> {
        match value {
            BasicValueEnum::IntValue(int_val) => {
                if int_val.get_type() == self.context.bool_type() {
                    Ok(int_val)
                } else {
                    let zero = int_val.get_type().const_int(0, false);
                    Ok(self
                        .builder
                        .build_int_compare(IntPredicate::NE, int_val, zero, name)?)
                }
            }
            _ => Err(CompilerError::internal(
                "Logical operations require boolean-compatible operands",
            )),
        }
    }

    fn generate_logical_and(
        &mut self,
        binary: &BinaryExpr,
        left: BasicValueEnum<'ctx>,
    ) -> Result<BasicValueEnum<'ctx>> {
        let current_block = self
            .builder
            .get_insert_block()
            .ok_or_else(|| CompilerError::internal("No current block for logical and"))?;
        let function = current_block.get_parent().ok_or_else(|| {
            CompilerError::internal("Current block missing parent function for logical and")
        })?;

        let rhs_block = self.context.append_basic_block(function, "logical.and.rhs");
        let merge_block = self.context.append_basic_block(function, "logical.and.merge");

        let left_bool = self.ensure_bool_value(left, "logical_and_left")?;
        let _ = self
            .builder
            .build_conditional_branch(left_bool, rhs_block, merge_block);

        // Right-hand side when left is true
        self.builder.position_at_end(rhs_block);
        let right_value = self.generate_expression(&binary.right)?;
        let right_bool = self.ensure_bool_value(right_value, "logical_and_right")?;
        let rhs_end_block = self.builder.get_insert_block().unwrap();
        let _ = self.builder.build_unconditional_branch(merge_block);

        // Merge results
        self.builder.position_at_end(merge_block);
        let phi = self
            .builder
            .build_phi(self.context.bool_type(), "logical_and_result")?;
        let false_value = self.context.bool_type().const_int(0, false);
        phi.add_incoming(&[(&right_bool, rhs_end_block), (&false_value, current_block)]);

        Ok(phi.as_basic_value())
    }

    fn generate_logical_or(
        &mut self,
        binary: &BinaryExpr,
        left: BasicValueEnum<'ctx>,
    ) -> Result<BasicValueEnum<'ctx>> {
        let current_block = self
            .builder
            .get_insert_block()
            .ok_or_else(|| CompilerError::internal("No current block for logical or"))?;
        let function = current_block.get_parent().ok_or_else(|| {
            CompilerError::internal("Current block missing parent function for logical or")
        })?;

        let rhs_block = self.context.append_basic_block(function, "logical.or.rhs");
        let merge_block = self.context.append_basic_block(function, "logical.or.merge");

        let left_bool = self.ensure_bool_value(left, "logical_or_left")?;
        let _ = self
            .builder
            .build_conditional_branch(left_bool, merge_block, rhs_block);

        // Evaluate right-hand side only when left is false
        self.builder.position_at_end(rhs_block);
        let right_value = self.generate_expression(&binary.right)?;
        let right_bool = self.ensure_bool_value(right_value, "logical_or_right")?;
        let rhs_end_block = self.builder.get_insert_block().unwrap();
        let _ = self.builder.build_unconditional_branch(merge_block);

        // Merge block selects true when left was true, otherwise RHS result
        self.builder.position_at_end(merge_block);
        let phi = self
            .builder
            .build_phi(self.context.bool_type(), "logical_or_result")?;
        let true_value = self.context.bool_type().const_int(1, false);
        phi.add_incoming(&[(&true_value, current_block), (&right_bool, rhs_end_block)]);

        Ok(phi.as_basic_value())
    }

    pub(in crate::llvm_backend) fn generate_binary_expression(
        &mut self,
        binary: &BinaryExpr,
    ) -> Result<BasicValueEnum<'ctx>> {
        let left = self.generate_expression(&binary.left)?;

        if matches!(binary.operator, BinaryOp::LogicalAnd) {
            return self.generate_logical_and(binary, left);
        }

        if matches!(binary.operator, BinaryOp::LogicalOr) {
            return self.generate_logical_or(binary, left);
        }

        let right = if matches!(binary.operator, BinaryOp::Assign) {
            match &*binary.right {
                Expression::Variable(var_name) => {
                    if let Some(var_value) = self.variable_map.get(var_name) {
                        if var_value.is_pointer_value() {
                            (*var_value).into()
                        } else {
                            (*var_value).into()
                        }
                    } else {
                        return Err(CompilerError::internal(&format!(
                            "Undefined variable: {}",
                            var_name
                        )));
                    }
                }
                _ => self.generate_expression(&binary.right)?,
            }
        } else {
            self.generate_expression(&binary.right)?
        };

        match binary.operator {
            BinaryOp::Assign => include!("arms/assign.rs"),
            BinaryOp::Add => include!("arms/add.rs"),
            BinaryOp::Sub => include!("arms/sub.rs"),
            BinaryOp::Mul => include!("arms/mul.rs"),
            BinaryOp::Div => include!("arms/div.rs"),
            BinaryOp::Mod => include!("arms/modulo.rs"),
            BinaryOp::Less => include!("arms/less.rs"),
            BinaryOp::LessEqual => include!("arms/lessequal.rs"),
            BinaryOp::Greater => include!("arms/greater.rs"),
            BinaryOp::GreaterEqual => include!("arms/greaterequal.rs"),
            BinaryOp::Equal => include!("arms/equal.rs"),
            BinaryOp::NotEqual => include!("arms/notequal.rs"),
            BinaryOp::AddAssign => include!("arms/addassign.rs"),
            BinaryOp::SubAssign => include!("arms/subassign.rs"),
            BinaryOp::MulAssign => include!("arms/mulassign.rs"),
            BinaryOp::DivAssign => include!("arms/divassign.rs"),
            BinaryOp::ModAssign => include!("arms/modassign.rs"),
            BinaryOp::BitAnd => include!("arms/bitand.rs"),
            BinaryOp::BitOr => include!("arms/bitor.rs"),
            BinaryOp::BitXor => include!("arms/bitxor.rs"),
            BinaryOp::LeftShift => include!("arms/leftshift.rs"),
            BinaryOp::RightShift => include!("arms/rightshift.rs"),
            BinaryOp::BitAndAssign => include!("arms/bitandassign.rs"),
            BinaryOp::BitOrAssign => include!("arms/bitorassign.rs"),
            BinaryOp::BitXorAssign => include!("arms/bitxorassign.rs"),
            BinaryOp::LeftShiftAssign => include!("arms/leftshiftassign.rs"),
            BinaryOp::RightShiftAssign => include!("arms/rightshiftassign.rs"),
            _ => Err(CompilerError::internal(&format!(
                "Unsupported binary operator: {:?}",
                binary.operator
            ))),
        }
    }
}
